#include "stdafx.h"
#include "inifile.h"
#include <algorithm>
#include <vector>
#include <sstream>

//#include "str.h"
namespace Zen{ namespace utility{ namespace ini{

#define DEF_SECT_NAME                  "General"
#define SECT_LEFT                      '['
#define SECT_RIGHT                     ']'
#define KEY_VALUE_SEP                  '='
#define LINE_SEP                       "\r\n"

///////////////////////////////////////////////////////////////////////////////
template <class KeyType, class ValueType>
class PairVector : public vector<pair<KeyType, ValueType> >
{
public:
    typedef vector<pair<KeyType, ValueType> >   super_type;
    typedef typename super_type::iterator       iterator;
    typedef typename super_type::value_type     value_type;
    inline iterator begin() { return super_type::begin(); }
    inline iterator end() { return super_type::end(); }

    ValueType& operator[](const KeyType& key)
    {
        for (iterator i = begin(), n = end(); i != n; ++i)
        {
            if (i->first == key)
                return i->second;
        }
        return super_type::insert(end(), value_type(key, ValueType()))->second;
    }

    iterator insert(iterator where, const value_type& value)
    {
        for (iterator i = begin(), n = end(); i != n; ++i)
        {
            if (i->first == value.first)
                return i;
        }
        return super_type::insert(where, value);
    }

    iterator find(const KeyType& key)
    {
        for (iterator i = begin(), n = end(); i != n; ++i)
        {
            if (i->first == key)
                return i;
        }
        return end();
    }

    void erase(const KeyType& key)
    {
        erase(find(key));
    }

    iterator erase(iterator where)
    {
        if (where != end())
            where = super_type::erase(where);
        return where;
    }
};
 
///////////////////////////////////////////////////////////////////////////////
typedef PairVector<string, string> Section;
typedef PairVector<string, Section> Sections;

///////////////////////////////////////////////////////////////////////////////
bool ParseLine(Section& section, const string& line)
{
    bool parsed = false;
    int pos = line.find_first_of(KEY_VALUE_SEP);
    if (pos == line.npos)
        return parsed;
    int end_pos = line.find_first_of("\r\n");
    string key = line.substr(0, pos);
    string value = line.substr(pos+1, end_pos-(pos+1));
    section[key] = value;
    return parsed = true;
}

bool IsSection(const string& line)
{
    return (!line.empty()) && (*line.begin() == SECT_LEFT);
}

string StripSectionName(const string& line)
{
    int pos = line.rfind(SECT_RIGHT);
    return line.substr(1, pos-1);
}

string WrapSectionName(const string& name)
{
    return string(1, SECT_LEFT) + name + SECT_RIGHT;
}

///////////////////////////////////////////////////////////////////////////////
class IniFilePrivate 
{
    friend class IniFile;

    Sections::iterator current_section;
    Sections sections;
};
    
///////////////////////////////////////////////////////////////////////////////
IniFile::IniFile()
    : m(*new IniFilePrivate())
{
    EndSection();
}

IniFile::~IniFile()
{
    delete &m;
}

bool IniFile::Load(istream& is)
{
    string line;
    Sections::iterator cursect = m.sections.end();

    bool is_loaded = !!is;
    while (!getline(is, line).fail())
    {
        if (IsSection(line))
        {
            cursect = m.sections.insert(m.sections.end(), make_pair(StripSectionName(line), Section()));
        }
        else if (cursect == m.sections.end())
        {
            is_loaded = false;
            break;
        }
        else
        {
            if (!ParseLine(cursect->second, line))
                continue;
        }
    }

    if (!is_loaded)
        m.sections.clear();
    return is_loaded;
}

bool IniFile::Save(ostream& os) const
{
    bool first = true;
    for (Sections::iterator cursect = m.sections.begin(); cursect != m.sections.end(); ++cursect)
    {
        if (cursect->first == DEF_SECT_NAME && cursect->second.empty())
        {
            continue;
        }
        else
        {
            if (first)
                first = false;
            else
                os<<LINE_SEP;
            os<<WrapSectionName(cursect->first)<<LINE_SEP;
            for (Section::iterator curkey = cursect->second.begin(); curkey != cursect->second.end(); ++curkey)
                os<<curkey->first<<KEY_VALUE_SEP<<curkey->second<<LINE_SEP;
        }
    }
    return !os.fail();
}

bool IniFile::DecryptLoad(istream& is)
{
    stringstream temp;
    return Decrypt(temp, is) && Load(temp);
}

bool IniFile::EncryptSave(ostream& os) const
{
    stringstream temp;
    return Save(temp) && Encrypt(os, temp);
}

bool IniFile::EncryptSave(ostream& os, const IniFile& other) const
{
    stringstream temp;
    return other.Save(temp) && Encrypt(os, temp);
}

int IniFile::GetNumber(const string& key, bool* ok/*=NULL*/) const
{
    return atoi(GetString(key, ok).c_str());
}

string IniFile::GetString(const string& key, bool* ok/*=NULL*/) const
{
    Section& cursect = m.current_section->second;
    Section::iterator curkey = cursect.find(key);

    if (ok)
        *ok = (curkey == cursect.end());

    if (curkey != cursect.end())
        return curkey->second;
    else
        return string();
}

bool IniFile::PutNumber(const string& key, int value, bool fail_if_exist/*=false*/)
{
	char buf[64];
    return PutString(key, itoa(value, buf, 10), fail_if_exist);
}

bool IniFile::PutString(const string& key, const string& value, bool fail_if_exist/*=false*/)
{
    Section& cursect = m.current_section->second;
    if (fail_if_exist)
    {
        if (cursect.find(key) != cursect.end())
            return false;
    }
    cursect[key] = value;
    return true;
}

void IniFile::RemoveKey(const string& key)
{
    m.current_section->second.erase(key);
}

void IniFile::RemoveSection(const string& section_name)
{
    m.sections.erase(section_name);
}

void IniFile::BeginSection(const string& section_name)
{
    m.current_section = m.sections.find(section_name);
    if (m.current_section == m.sections.end())
        m.current_section = m.sections.insert(m.current_section, make_pair(section_name, Section()));
}

void IniFile::EndSection()
{
    BeginSection(DEF_SECT_NAME);
}

bool IniFile::Decrypt(ostream& decrypted, istream& encrypted) const
{
    return false;
}

bool IniFile::Encrypt(ostream& encrypted, istream& decrypted) const
{
    return false;
}

}}}